﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace AccessControl.GateTest
{
    public class GateCommiunicatorClient
    {
        #region fields

        private readonly TcpClient client;
        private Thread clientThread;
        private NetworkStream clientStream;
        private IPEndPoint _ipEndpoint;
        #endregion

        #region Properties

        public bool AutoReconnect { get; set; }
        //private int _timeout = 10000;
        //public int Timeout
        //{
        //    get { return _timeout = 10000; }
        //    set { _timeout = value; }
        //}

        #endregion

        #region Events

        public delegate void DataReceivedEventHandler(object sender, string Data);
        public event DataReceivedEventHandler DataReceived;
        public void OnDataReceived(string data)
        {
            if (DataReceived != null) DataReceived(this, data);
        }

        public event DataReceivedEventHandler CardDetected;
        public void OnCardDetected(string cardID)
        {
            if (CardDetected != null) CardDetected(this, cardID);
        }

        public event EventHandler StatusChanged;
        public void OnStatusChanged()
        {
            if (StatusChanged != null) StatusChanged(this, EventArgs.Empty);
        }
        #endregion

        #region Constructors

        public GateCommiunicatorClient()
        {
            client = new TcpClient();

        }

        public GateCommiunicatorClient(string ipString, int port)
            : this()
        {
            Connect(ipString, port);
        }

        #endregion

        #region Methods
        public bool Connect(string ipString, int port){
            AutoReconnect = true;

            if (client.Connected)
                throw new ApplicationException("Gate is already connected!");

            try{
                _ipEndpoint = new IPEndPoint(IPAddress.Parse(ipString), port);
                Connect(_ipEndpoint);

                clientThread = new Thread(Receive);
                clientThread.Start();

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        public bool Connect(IPEndPoint ipEndPoint){
            try{
                client.Connect(ipEndPoint);

                clientStream = client.GetStream();

                return true;
            }
            catch (Exception){

                return false;
            }

        }

        public void Reconnect(){
            if(client.Connected) client.Close();

            Connect(_ipEndpoint);
        }

        public bool CheckConnection(){
            while (!client.Connected){
                Reconnect();

                if (!AutoReconnect) return false;

                Thread.Sleep(1000);
            }
            return true;
        }

        public void Disconnect()
        {
            AutoReconnect = false;
            if(client.Connected) client.Close();
        }

        private void Receive()
        {

            var receiveBuffer = new byte[client.Client.ReceiveBufferSize];
            var Data = "";
            //client.NoDelay = true;

            while (AutoReconnect)
            {

                try
                {
                    while (client.Connected)
                    {

                        var availableDataSize = clientStream.Read(receiveBuffer, 0, client.Available);
                        Data += Encoding.ASCII.GetString(receiveBuffer, 0, availableDataSize);

                        if (!Data.EndsWith("]")) continue;

                        OnDataReceived(Data);
                        OnCardDetected(Data.Substring(5, Data.IndexOf("]") - 5));
                        Data = "";


                    }

                }
                catch (Exception)
                {

                    OnDataReceived("Disconnected");

                }
                Thread.Sleep(1000);
                Reconnect();
            }
        }

        public void Send(string command)
        {
            if (!CheckConnection()) return;

            var data = Encoding.ASCII.GetBytes(command);

                clientStream.Write(data, 0, data.Length);

            
        }

        public void AllowGate()
        {
            Send("[02011]");
        }

        public void DenyGate()
        {
            Send("[02010]");
        }


        #endregion



    }
}
